#include <iostream>
#include <cstdio>
#include <signal.h>
#include <unistd.h>
#include <sys/types.h>
#include <cstring>
#include <sys/wait.h>
#include <cassert>

// sigset_t pending;
// volatile int flag = 0; //保证内存可见性
pid_t id;

void PrintPending(const sigset_t &pending)
{
    std::cout << "进程pid:" << getpid() << " ";
    for (int signo = 1; signo <= 31; signo++)
    {
        if (sigismember(&pending, signo) == 1)
            std::cout << 1;
        else
            std::cout << 0;
    }
    std::cout << "\n";
}

void handler(int signo)
{
    // 只获取不终止程序会造成死循环
    // std::cout << "get signo is: " << signo << std::endl;
    // exit(-1);
    // std::cout << "\n检验信号1~0的转换\n";
    // PrintPending(pending); //信号是在被捕获后，pending表由1变成0
    // std::cout << std::endl;
    // int cnt = 10;
    // while (cnt--)
    // {
    //     sigset_t pending;
    //     sigemptyset(&pending);

    //     // 获取pending表
    //     int n = sigpending(&pending);
    //     assert(n != -1);
    //     (void)n;
    //     PrintPending(pending);
    //     sleep(1);
    // }
    // std::cout << "捕获到信号 :" << signo << std::endl;

    // 在下编译器会优化,获取到信号后没有任何动作
    // CUP只在寄存器中获取flag值，寄存器中的flag值一直为0，尽管内容中是修改了flag的值
    // 防止flag值一直没有更新，可以加上volatile关键字
    //  flag = 1;
    //  std::cout << "change flag from 0 to 1...\n";

    std::cout << "收到来自子进程退出信号：" << signo << ", 我的pid:" << getpid() << std::endl;
    // 查看子进程僵尸状态
    sleep(5);

    //-1表示回收任意的子进程
    // int res = waitpid(-1, nullptr, 0);
    // if (res > 0)
    //     std::cout << "回收子进程成功，res：" << res << ", pid:" << id << std::endl;
    // else
    //     std::cout << "回收子进程失败， res：" << res << std::endl;

    // 循环等待回收子进程，信号抵达就回收
    while (true)
    {   
        //在这里不应该使用阻塞等待，当有一部分进程退出了，一部分还在运转，也会造成内存泄漏
        //pid_t res = waitpid(-1, nullptr, 0);
        pid_t res = waitpid(-1, nullptr, WNOHANG);

        if (res > 0)
        {
            std::cout << "回收子进程成功，res：" << res << ", pid:" << id << std::endl; 
        }
        else
        {
            std::cout << "没有可回收的子进程." << res << std::endl;
            break;
        }
    }
    std::cout << "handler done...\n";
}

// void showBlock(sigset_t* oset)
// {
//     for(int signo = 1; signo <= 31; signo++)
//     {
//         if(sigismember(oset, signo)) std::cout << "1";
//         else std::cout << "0";
//     }
// }

// 证明：子进程退出，给父进程发了信号
int main()
{
    // 子进程退出会发出17号信号
    // signal(SIGCHLD, handler);
    //signal(17, handler);

    //在Linux下，可以让子进程防止进入僵尸状态:SIG_IGN表示忽略的含义
    signal(SIGCHLD, SIG_IGN);

    // 创建进程池
    for (int i = 0; i < 10; i++)
    {
        id = fork();
        if (id == 0)
        {
            // 子进程
            int cnt = 5;
            while (cnt--)
            {
                std::cout << "我是子进程，我的pid是：" << getpid() << std::endl;
                sleep(1);
            }
            // 多个进程同时退出，父进程会受到多份SIGCHLD信号
            // 在处理一个信号时，其他信号是被阻塞的，会导致多个进程没有被及时回收，内存泄漏
            exit(1);
        }
    }

    // id = fork();
    // if(id == 0)
    // {
    //     //子进程
    //     int cnt = 5;
    //     while(cnt--)
    //     {
    //         std::cout << "我是子进程，我的pid是：" << getpid() << std::endl;
    //         sleep(1);
    //     }
    //     exit(1);
    // }

    // 父进程
    while (true)
    {
        sleep(1); // 避免父进程退出
    }

    return 0;
}

// 测试volatile关键字
//  int main()
//  {
//      signal(2, handler);

//     while(!flag);
//     std::cout << "flag:" << flag << std::endl;

//     return 0;
// }

// int main()
// {
// 使用sigaction系统调用对信号进行捕捉
// struct sigaction act, oact;
// // 初始化
// memset(&act, 0, sizeof(act));
// memset(&oact, 0, sizeof(act));

// act.sa_handler = handler; // 使用函数指针（赋值）,捕获到信号后的处理的方式
// act.sa_flags = 0;         // 不做特殊处理

// // 对2号信号进行阻塞
// sigemptyset(&act.sa_mask);

// // 捕获信号
// sigaction(SIGINT, &act, &oact);
// sigaction(3, &act, &oact);
// sigaction(5, &act, &oact);
// sigaction(4, &act, &oact);

// //以上动作犹如 signal 函数

// while (true)
// {
//     std::cout << "进程pid:" << getpid() << std::endl;
//     sleep(1);
// }

//     // 自定义捕捉信号
//     signal(2, handler);
//     // sigpending表
//     sigset_t set, oset;
//     // 初始化
//     sigemptyset(&set);
//     sigemptyset(&oset);
//     // 将2号信号添加到信号集
//     sigaddset(&set, SIGINT);

//     sigaddset(&set, 3);
//     sigaddset(&set, 9);

//     // 使当前进程阻塞二号信号:将set信号集设置到block表
//     sigprocmask(SIG_BLOCK, &set, &oset);

//     int cnt = 0;
//     while (true)
//     {
//         // sigset_t pending;
//         sigemptyset(&pending);
//         // 获取pending表
//         int n = sigpending(&pending);
//         assert(n != -1);
//         (void)n; // release版本下让编译器认为这个变量使用了，防止waring出现
//         // 打印pending表
//         PrintPending(pending);

//         if (cnt++ == 5)
//         {
//             std::cout << "信号的阻塞状态解除,信号即将抵达......\n";
//             sleep(1);
//             // 恢复信号
//             sigprocmask(SIG_SETMASK, &oset, &set);
//         }

//         sleep(1);
//     }

// 阻塞信号：block表的位图设置
// 信号集：sigset_t
//  sigset_t set, oset;
//  //信号集清空
//  sigemptyset(&set);
//  sigemptyset(&oset);
//  //设置将2号信号设入信号集
//  sigaddset(&set, 2);
//  //让进程阻塞2号信号
//  sigprocmask(SIG_SETMASK, &set, &oset);

// int count = 0;
// while(true)
// {
//     showBlock(&oset);
//     sleep(1);

//     if(count++ == 10)
//     {
//         //将信号去阻塞
//         sigprocmask(SIG_SETMASK, &oset, &set);
//         showBlock(&set);
//     }
// }

// 测试
// signal(2, SIG_DFL); //2号信号的默认动作
//  signal(2, SIG_IGN); //SIG_IGN：忽略2号信号

// int count = 0;
// while(true)
// {
//     std::cout << count++ << std::endl;
//     sleep(1);
// }

// 验证core dump标志位
//  int id = fork();

// if(id == 0)
// {
//     //子进程
//     while(true)
//     {
//         int* p = nullptr;
//         std::cout << "野指针问题：*p=100\n";
//         std::cout << "野指针问题：*p=100\n";
//         std::cout << "野指针问题：*p=100\n";
//         *p = 100;
//         std::cout << "野指针问题：*p=100\n";
//         std::cout << "野指针问题：*p=100\n";
//     }
//     exit(0);
// }

// int status = 0;
// //等待子进程
// waitpid(id, &status, 0);
// std::cout << "退出码：" << ((status>>8) & 0xFF) << std::endl;
// std::cout << "退出信号：" << (status & 0x7F) << std::endl;
// std::cout << "core dump标志位：" << ((status>>7)&0x1) << std::endl;
// 开启核心转储后，core dump标志位为1
// 关闭核心转储后，core dump标志位为0

// 开启核心转储：ulimit -c 大小
// 关闭核心转储：ulimit -c 0
// Term：终止进程，没有多余动作
// Core：终止进程，如果开启了核心转储，会生成core-file；
// core-file便于debug调试排错，不好的是，一旦出现异常就会生成这个文件，占用磁盘空间

// 野指针问题：CPU内部的MMU硬件异常导致，OS给进程发送：SIGSEGV(11号信号) Core
// signal(SIGSEGV, handler);
// int* p = nullptr;
// p = 100; 非法操作，编译报错
//*p = 100; //编译没有报错，运行段错误:越界访问
//  while(true)
//  {
//      int* p = nullptr;
//      std::cout << "野指针问题：*p=100\n";
//      std::cout << "野指针问题：*p=100\n";
//      std::cout << "野指针问题：*p=100\n";
//      *p = 100;
//      std::cout << "野指针问题：*p=100\n";
//      std::cout << "野指针问题：*p=100\n";
//  }

// 除0操作:触发硬件异常，OS发送SIGFPE(8号信号) Core
//  signal(SIGFPE, handler); //自定义动作
//  int a = 10;
//  int b = a / 0;   //Floating point exception

//     return 0;
// }